﻿using System;
using System.CodeDom;
using System.Linq;
using Roslyn.Compilers.CSharp;

namespace InterfaceWeaver.CustomTool
{
    public static class AttributeSyntaxExtensions
    {
        /// <summary>
        /// Determines whether the specified attribute is of type.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="attribute">The attribute.</param>
        /// <returns>
        ///   <c>true</c> if is of type the specified attribute; otherwise, <c>false</c>.
        /// </returns>
        public static bool IsOfType<T>(this AttributeSyntax attribute) where T: Attribute
        {
            var typeLongName = typeof(T).Name;
            var typeShortName = typeLongName.EndsWith("Attribute")
                ? typeLongName.Substring(0, typeLongName.Length - "Attribute".Length)
                : typeLongName;
            var attributeName = attribute.Name.ToFullString().Trim();
            return attributeName == typeShortName || attributeName == typeLongName;
        }

        /// <summary>
        /// Determines whether the specified attribute has parameter.
        /// </summary>
        /// <param name="attribute">The attribute.</param>
        /// <param name="name">The name.</param>
        /// <returns>
        ///   <c>true</c> if the specified attribute has parameter; otherwise, <c>false</c>.
        /// </returns>
        public static bool HasParameter(this AttributeSyntax attribute, string name)
        {
            return attribute.ArgumentList != null && attribute.ArgumentList.Arguments.Any(arg => arg.GetName() == name);
        }

        /// <summary>
        /// Gets the parameter value.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="attribute">The attribute.</param>
        /// <param name="name">The name of the parameter.</param>
        /// <returns></returns>
        public static T GetParameterValue<T>(this AttributeSyntax attribute, string name)
        {
            var argument = attribute.ArgumentList.Arguments.FirstOrDefault(arg => arg.GetName() == name);
            if(argument != null && argument.Expression is LiteralExpressionSyntax)
            {
                try
                {
                    return (T) ((LiteralExpressionSyntax) argument.Expression).CompileValue();
                }
                catch
                {
                    return default(T);
                }
            }

            return default(T);
        }

        /// <summary>
        /// Gets the parameter value.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="attribute">The attribute.</param>
        /// <returns></returns>
        public static T GetParameterValue<T>(this AttributeSyntax attribute)
        {
            var argument = attribute.ArgumentList.Arguments.FirstOrDefault();
            if (argument != null && argument.Expression is LiteralExpressionSyntax)
            {
                try
                {
                    return (T)((LiteralExpressionSyntax)argument.Expression).CompileValue();
                }
                catch
                {
                    return default(T);
                }
            }

            return default(T);
        }

        /// <summary>
        /// Gets the parameter type of.
        /// </summary>
        /// <param name="attribute">The attribute.</param>
        /// <param name="name">The name.</param>
        /// <returns></returns>
        public static TypeSyntax GetParameterTypeOf(this AttributeSyntax attribute, string name)
        {
            var argument = attribute.ArgumentList.Arguments.FirstOrDefault(arg => arg.GetName() == name);
            if (argument != null)
            {
                return ((TypeOfExpressionSyntax)argument.Expression).Type;
            }

            return null;
        }

        /// <summary>
        /// Gets the parameter type of.
        /// </summary>
        /// <param name="attribute">The attribute.</param>
        /// <returns></returns>
        public static TypeSyntax GetParameterTypeOf(this AttributeSyntax attribute)
        {
            var argument = attribute.ArgumentList.Arguments.FirstOrDefault();
            if (argument != null)
            {
                return ((TypeOfExpressionSyntax)argument.Expression).Type;
            }

            return null;
        }


        /// <summary>
        /// Gets the parameter value text.
        /// </summary>
        /// <param name="attribute">The attribute.</param>
        /// <param name="name">The name.</param>
        /// <returns></returns>
        public static string GetParameterValueText(this AttributeSyntax attribute, string name)
        {
            var argument = attribute.ArgumentList.Arguments.FirstOrDefault(arg => arg.GetName() == name);
            if (argument != null)
            {
                return argument.Expression.ToFullString().Trim();
            }

            return null;
        }

        /// <summary>
        /// Gets the name of the parameter type of.
        /// </summary>
        /// <param name="attribute">The attribute.</param>
        /// <param name="name">The name.</param>
        /// <returns></returns>
        public static string GetParameterTypeOfName(this AttributeSyntax attribute, string name)
        {
            var type = attribute.GetParameterTypeOf(name);
            if (type != null)
            {
                return type.ToFullString().Trim();
            }

            return null;
        }

        /// <summary>
        /// Gets the name of the parameter type of.
        /// </summary>
        /// <param name="attribute">The attribute.</param>
        /// <returns></returns>
        public static string GetParameterTypeOfName(this AttributeSyntax attribute)
        {
            var type = attribute.GetParameterTypeOf();
            if (type != null)
            {
                return type.ToFullString().Trim();
            }

            return null;
        }

        /// <summary>
        /// Creates the attribute declaration from attribute syntx.
        /// </summary>
        /// <param name="attributeSyntax">The attribute syntax.</param>
        /// <returns></returns>
        public static CodeAttributeDeclaration CreateAttributeDeclaration(this AttributeSyntax attributeSyntax)
        {
            var attributeDeclaration = new CodeAttributeDeclaration(attributeSyntax.Name.ToFullString().Trim());
            if (attributeSyntax.ArgumentList != null)
            {
                foreach (var argument in attributeSyntax.ArgumentList.Arguments)
                {
                    var argumentName = argument.GetName();
                    CodeExpression argumentExpression = null;

                    if (argument.Expression is TypeOfExpressionSyntax)
                    {
                        argumentExpression =
                            new CodeTypeOfExpression(
                                ((TypeOfExpressionSyntax) argument.Expression).Type.ToFullString().Trim());
                    }
                    else if (argument.Expression is LiteralExpressionSyntax)
                    {
                        argumentExpression =
                            new CodePrimitiveExpression(((LiteralExpressionSyntax) argument.Expression).CompileValue());
                    }
                    else
                    {
                        return null;
                    }

                    attributeDeclaration.Arguments.Add(argumentName == null
                                                           ? new CodeAttributeArgument(argumentExpression)
                                                           : new CodeAttributeArgument(argumentName, argumentExpression));
                }
            }

            return attributeDeclaration;

        }
    }
}
